﻿using System;
using System.Collections.Generic;
using System.IO;
using Drill.Configuration.ConfigFile;
using Drill.Core;
using NUnit.Framework;

namespace Drill.Configuration
{
	[TestFixture]
	public class ConfigurationHelperTests
	{
		[Test]
		public void CanConstructWithListOfPathProvidersAndOmittedSearchPaths()
		{
			// Arrange
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};

			// Act
			IConfigurationHelper configurationHelper = new ConfigurationHelper(pathProviders);

			// Assert
			Assert.That(configurationHelper, Is.Not.Null);
		}

		[Test]
		public void CanConstructWithListOfPathProvidersAndExplicitNullSearchPaths()
		{
			// Arrange
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			List<ISearchPath> searchPaths = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			IConfigurationHelper configurationHelper = new ConfigurationHelper(pathProviders, searchPaths);
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			Assert.That(configurationHelper, Is.Not.Null);
		}

		[Test]
		public void CanConstructWithListOfPathProvidersAndEmptySearchPaths()
		{
			// Arrange
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			List<ISearchPath> searchPaths = new List<ISearchPath>();

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			IConfigurationHelper configurationHelper = new ConfigurationHelper(pathProviders, searchPaths);
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			Assert.That(configurationHelper, Is.Not.Null);
		}

		[Test]
		public void CanConstructWithListOfPathProvidersAndSearchPaths()
		{
			// Arrange
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			List<ISearchPath> searchPaths = new List<ISearchPath> { new SearchPath(@"SomeFakeDirectory\Config") };

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			IConfigurationHelper configurationHelper = new ConfigurationHelper(pathProviders, searchPaths);
			// ReSharper restore ExpressionIsAlwaysNull

			// Assert
			Assert.That(configurationHelper, Is.Not.Null);
		}

		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "rootPathProviders", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotCreateInstanceWithNullListOfPathProvider()
		{
			// Arrange
			List<IRootPathProvider> pathProviders = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			// ReSharper disable ObjectCreationAsStatement
			new ConfigurationHelper(pathProviders);
			// ReSharper restore ObjectCreationAsStatement
			// ReSharper restore ExpressionIsAlwaysNull

			// Arrange
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "The rootPathProviders list is empty. The rootPathProviders list must contain at least one", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotCreateInstanceWithEmptyListOfPathProvider()
		{
			// Arrange
			List<IRootPathProvider> pathProviders = new List<IRootPathProvider>();

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			// ReSharper disable ObjectCreationAsStatement
			new ConfigurationHelper(pathProviders);
			// ReSharper restore ObjectCreationAsStatement
			// ReSharper restore ExpressionIsAlwaysNull

			// Arrange
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		private static IConfigurationHelper CreateValidConfigurationHelperWithoutSearchPaths()
		{
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			return new ConfigurationHelper(pathProviders);
		}

		private static IConfigurationHelper CreateValidConfigurationHelperWithSearchPaths()
		{
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			List<ISearchPath> searchPaths = 
				new List<ISearchPath>
					{
						new SearchPath(@"FakeDir\FakeSubDir")
					  , new SearchPath(@"..\Config")
					};
			return new ConfigurationHelper(pathProviders, searchPaths);
		}

		private static IConfigurationHelper CreateValidConfigurationHelperWithSearchPathsContainingOneAbsoluteValidFilename()
		{
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			List<ISearchPath> searchPaths =
				new List<ISearchPath>
					{
						new SearchPath(@"FakeDir\FakeSubDir")
					  , new SearchPath(@"..\Config")
					};
			return new ConfigurationHelper(pathProviders, searchPaths);
		}

		private static IConfigurationHelper CreateValidConfigurationHelperWithSearchPathsContainingOneAbsoluteValidDirectory()
		{
			List<IRootPathProvider> pathProviders =
				new List<IRootPathProvider>
					{
						new ExecutingDirectoryRootPathProvider()
					};
			List<ISearchPath> searchPaths =
				new List<ISearchPath>
					{
						new SearchPath(@"FakeDir\FakeSubDir")
					  , new SearchPath(Path.GetDirectoryName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile))
					  , new SearchPath(@"..\Config")
					};
			return new ConfigurationHelper(pathProviders, searchPaths);
		}

		[Test]
		public void CanGetConfigSectionFromConfigFileGenericWithNoArguments()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();

			// Act
			DependencyResolversConfigurationSection configSection =
				configurationHelper.GetConfigSectionFromConfigFile<DependencyResolversConfigurationSection>();

			// Assert
			Assert.That(configSection, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers.Count, Is.EqualTo(1));
			Assert.That(configSection.DependencyResolvers[0].Name, Is.EqualTo("Default"));
		}

		[Test]
		public void CanGetConfigSectionFromConfigFileGenericWithFilenameArgument()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();
			string filename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Act
			DependencyResolversConfigurationSection configSection =
				configurationHelper.GetConfigSectionFromConfigFile<DependencyResolversConfigurationSection>(filename);

			// Assert
			Assert.That(configSection, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers.Count, Is.EqualTo(1));
			Assert.That(configSection.DependencyResolvers[0].Name, Is.EqualTo("Default"));
		}

		[Test]
		public void CanGetConfigSectionFromConfigFileWithConfigSectionNameArgument()
		{
			// Arrange
			const string configSectionName = @"drill/dependencyResolvers";
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();

			// Act
			DependencyResolversConfigurationSection configSection =
				(DependencyResolversConfigurationSection)configurationHelper.GetConfigSectionFromConfigFile(configSectionName);

			// Assert
			Assert.That(configSection, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers.Count, Is.EqualTo(1));
			Assert.That(configSection.DependencyResolvers[0].Name, Is.EqualTo("Default"));
		}

		[Test]
		public void CanGetConfigSectionFromConfigFileWithConfigSectionNameAndFilenameArguments()
		{
			// Arrange
			const string configSectionName = @"drill/dependencyResolvers";
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();
			string filename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Act
			DependencyResolversConfigurationSection configSection =
				(DependencyResolversConfigurationSection)configurationHelper.GetConfigSectionFromConfigFile(configSectionName, filename);

			// Assert
			Assert.That(configSection, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers, Is.Not.Null);
			Assert.That(configSection.DependencyResolvers.Count, Is.EqualTo(1));
			Assert.That(configSection.DependencyResolvers[0].Name, Is.EqualTo("Default"));
		}

		[Test]
		public void CanLoadConfigFileWithFilenameArgument()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();
			string filename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Act
			System.Configuration.Configuration configuration =
				configurationHelper.LoadConfigFile(filename);

			// Assert
			Assert.That(configuration, Is.Not.Null);
		}

		[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "filename", MatchType = MessageMatch.Contains)]
		[Test]
		public void CannotFindAbsolutePathWithNullFilenameArgument()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();
			string filename = null;

			// Act
			// ReSharper disable ExpressionIsAlwaysNull
			configurationHelper.FindAbsolutePath(filename);
			// ReSharper restore ExpressionIsAlwaysNull

			// Arrange
			// Nothing to assert because the test should throw an expected exception (see ExpectedException attribute).
		}

		[Test]
		public void CanFindAbsolutePathWithValidAbsoluteFilenameArgument()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();
			string filename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Act
			string absolutePath = configurationHelper.FindAbsolutePath(filename);

			// Assert
			Assert.That(absolutePath, Is.EqualTo(filename));
		}

		[Test]
		public void CanFindAbsolutePathWithoutSearchPaths()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithoutSearchPaths();
			string filename = Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

			// Act
			string absolutePath = configurationHelper.FindAbsolutePath(filename);

			// Assert
			Assert.That(absolutePath, Is.Not.Null.Or.Empty);
			Assert.That(File.Exists(absolutePath), Is.True);
		}

		[Test]
		public void CanFindAbsolutePathWithSearchPaths()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithSearchPaths();
			string filename = Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

			// Act
			string absolutePath = configurationHelper.FindAbsolutePath(filename);

			// Assert
			Assert.That(absolutePath, Is.Not.Null.Or.Empty);
			Assert.That(File.Exists(absolutePath), Is.True);
		}

		[Test]
		public void CanFindAbsolutePathWithSearchPathsContainingOneAbsoluteValidFilename()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithSearchPathsContainingOneAbsoluteValidFilename();
			string filename = Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

			// Act
			string absolutePath = configurationHelper.FindAbsolutePath(filename);

			// Assert
			Assert.That(absolutePath, Is.Not.Null.Or.Empty);
			Assert.That(File.Exists(absolutePath), Is.True);
		}

		[Test]
		public void CanFindAbsolutePathWithSearchPathsContainingOneAbsoluteValidDirectory()
		{
			// Arrange
			IConfigurationHelper configurationHelper = CreateValidConfigurationHelperWithSearchPathsContainingOneAbsoluteValidDirectory();
			string filename = Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

			// Act
			string absolutePath = configurationHelper.FindAbsolutePath(filename);

			// Assert
			Assert.That(absolutePath, Is.Not.Null.Or.Empty);
			Assert.That(File.Exists(absolutePath), Is.True);
		}
	}
}